1use crate::ext::io::*;
3use crate::scripts::base::*;
4use crate::types::*;
5use crate::utils::img::*;
6use crate::utils::struct_pack::*;
7use anyhow::Result;
8use clap::ValueEnum;
9use clap::builder::PossibleValue;
10use msg_tool_macro::*;
11use overf::wrapping;
12use std::io::{Read, Seek, Write};
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum CircusCrxMode {
17 Fixed(u8),
19 Auto,
21 Origin,
23 Best,
25}
26
27impl Default for CircusCrxMode {
28 fn default() -> Self {
29 CircusCrxMode::Auto
30 }
31}
32
33impl CircusCrxMode {
34 pub fn for_importing(&self) -> Self {
36 match self {
37 CircusCrxMode::Auto => CircusCrxMode::Origin,
38 _ => *self,
39 }
40 }
41
42 pub fn for_creating(&self) -> Self {
44 match self {
45 CircusCrxMode::Auto => CircusCrxMode::Best,
46 CircusCrxMode::Origin => CircusCrxMode::Best,
47 _ => *self,
48 }
49 }
50
51 pub fn is_best(&self) -> bool {
53 matches!(self, CircusCrxMode::Best)
54 }
55
56 pub fn is_origin(&self) -> bool {
58 matches!(self, CircusCrxMode::Origin)
59 }
60}
61
62impl ValueEnum for CircusCrxMode {
63 fn value_variants<'a>() -> &'a [Self] {
64 &[
65 CircusCrxMode::Fixed(0),
66 CircusCrxMode::Fixed(1),
67 CircusCrxMode::Fixed(2),
68 CircusCrxMode::Fixed(3),
69 CircusCrxMode::Fixed(4),
70 CircusCrxMode::Auto,
71 CircusCrxMode::Origin,
72 CircusCrxMode::Best,
73 ]
74 }
75
76 fn to_possible_value(&self) -> Option<PossibleValue> {
77 Some(match self {
78 CircusCrxMode::Fixed(0) => PossibleValue::new("0").help("Row type 0"),
79 CircusCrxMode::Fixed(1) => PossibleValue::new("1").help("Row type 1"),
80 CircusCrxMode::Fixed(2) => PossibleValue::new("2").help("Row type 2"),
81 CircusCrxMode::Fixed(3) => PossibleValue::new("3").help("Row type 3"),
82 CircusCrxMode::Fixed(4) => PossibleValue::new("4").help("Row type 4"),
83 CircusCrxMode::Auto => PossibleValue::new("auto")
84 .help("When importing, use origin mode, otherwise use best mode."),
85 CircusCrxMode::Origin => PossibleValue::new("origin")
86 .help("Use origin mode for importing. When creating, fallback to best mode."),
87 CircusCrxMode::Best => PossibleValue::new("best").help("Try to use the best mode."),
88 _ => return None,
89 })
90 }
91}
92
93#[derive(Debug)]
94pub struct CrxImageBuilder {}
96
97impl CrxImageBuilder {
98 pub const fn new() -> Self {
100 CrxImageBuilder {}
101 }
102}
103
104impl ScriptBuilder for CrxImageBuilder {
105 fn default_encoding(&self) -> Encoding {
106 Encoding::Cp932
107 }
108
109 fn build_script(
110 &self,
111 data: Vec<u8>,
112 _filename: &str,
113 _encoding: Encoding,
114 _archive_encoding: Encoding,
115 config: &ExtraConfig,
116 _archive: Option<&Box<dyn Script>>,
117 ) -> Result<Box<dyn Script>> {
118 Ok(Box::new(CrxImage::new(MemReader::new(data), config)?))
119 }
120
121 fn extensions(&self) -> &'static [&'static str] {
122 &["crx"]
123 }
124
125 fn script_type(&self) -> &'static ScriptType {
126 &ScriptType::CircusCrx
127 }
128
129 fn is_image(&self) -> bool {
130 true
131 }
132
133 fn is_this_format(&self, _filename: &str, buf: &[u8], buf_len: usize) -> Option<u8> {
134 if buf_len >= 4 && buf.starts_with(b"CRXG") {
135 return Some(255);
136 }
137 None
138 }
139
140 fn can_create_image_file(&self) -> bool {
141 true
142 }
143
144 fn create_image_file<'a>(
145 &'a self,
146 data: ImageData,
147 _filename: &str,
148 writer: Box<dyn WriteSeek + 'a>,
149 options: &ExtraConfig,
150 ) -> Result<()> {
151 CrxImage::create_image(data, writer, options)
152 }
153}
154
155#[derive(Clone, Debug, StructPack, StructUnpack)]
156struct Clip {
157 field_0: u32,
158 img_width: u16,
159 img_height: u16,
160 clip_offset_x: u16,
161 clip_offset_y: u16,
162 clip_width: u16,
163 clip_height: u16,
164}
165
166#[derive(Clone, Debug, StructPack, StructUnpack)]
167struct Header {
168 inner_x: u16,
169 inner_y: u16,
170 width: u16,
171 height: u16,
172 version: u16,
173 flags: u16,
174 bpp: u16,
175 mode: u16,
176 #[skip_pack_if(self.version != 3)]
177 #[skip_unpack_if(version != 3)]
178 #[pvec(u32)]
179 clips: Vec<Clip>,
180}
181
182#[derive(Clone, Debug)]
183enum CrxImageData {
184 RowEncoded(Vec<u8>),
185 IndexedV1 {
186 pixels: Vec<u8>,
187 stride: usize,
188 palette: Vec<u8>,
189 palette_format: PaletteFormat,
190 pixel_depth_bits: usize,
191 },
192 Direct(Vec<u8>),
193}
194
195impl CrxImageData {
196 fn is_row_encoded(&self) -> bool {
197 matches!(self, CrxImageData::RowEncoded(_))
198 }
199}
200
201pub struct CrxImage {
203 header: Header,
204 color_type: ImageColorType,
205 data: CrxImageData,
206 compress_level: u32,
207 keep_original_bpp: bool,
208 zstd: bool,
209 zstd_compression_level: i32,
210 row_type: CircusCrxMode,
211 canvas: bool,
212}
213
214impl std::fmt::Debug for CrxImage {
215 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
216 let data_info = match &self.data {
217 CrxImageData::RowEncoded(buf) => format!("row-encoded({})", buf.len()),
218 CrxImageData::IndexedV1 { pixels, .. } => {
219 format!("indexed-v1({})", pixels.len())
220 }
221 CrxImageData::Direct(buf) => format!("direct({})", buf.len()),
222 };
223 f.debug_struct("CrxImage")
224 .field("header", &self.header)
225 .field("color_type", &self.color_type)
226 .field("data", &data_info)
227 .finish()
228 }
229}
230
231impl CrxImage {
232 pub fn new<T: Read + Seek>(data: T, config: &ExtraConfig) -> Result<Self> {
237 let mut reader = data;
238 let mut magic = [0; 4];
239 reader.read_exact(&mut magic)?;
240 if magic != *b"CRXG" {
241 return Err(anyhow::anyhow!("Invalid CRX image magic"));
242 }
243 let header: Header = reader.read_struct(false, Encoding::Utf8, &None)?;
244 if header.version == 0 || header.version > 3 {
245 return Err(anyhow::anyhow!(
246 "Unsupported CRX version: {}",
247 header.version
248 ));
249 }
250
251 let (color_type, data) = if header.version == 1 {
252 let width = usize::from(header.width);
253 let height = usize::from(header.height);
254 if width == 0 || height == 0 {
255 return Err(anyhow::anyhow!("CRX v1 image has zero dimensions"));
256 }
257
258 let bits_per_pixel = match header.bpp {
259 0 => 24usize,
260 1 => 32usize,
261 _ => 8usize,
262 };
263 if bits_per_pixel % 8 != 0 {
264 return Err(anyhow::anyhow!(
265 "Unsupported bits per pixel {} for CRX v1",
266 bits_per_pixel
267 ));
268 }
269 let pixel_size = bits_per_pixel / 8;
270 if pixel_size == 0 {
271 return Err(anyhow::anyhow!("Invalid pixel size for CRX v1 image"));
272 }
273
274 let row_bytes = width
275 .checked_mul(pixel_size)
276 .ok_or_else(|| anyhow::anyhow!("CRX v1 row size overflow"))?;
277 let stride = (row_bytes
278 .checked_add(3)
279 .ok_or_else(|| anyhow::anyhow!("CRX v1 stride overflow"))?)
280 & !3usize;
281 let output_len = stride
282 .checked_mul(height)
283 .ok_or_else(|| anyhow::anyhow!("CRX v1 buffer size overflow"))?;
284
285 let palette = if bits_per_pixel == 8 {
286 let raw_colors = usize::from(header.bpp);
287 Some((
288 Self::read_v1_palette(&mut reader, raw_colors)?,
289 PaletteFormat::Rgb,
290 ))
291 } else {
292 None
293 };
294
295 if (header.flags & 0x10) != 0 {
296 reader.read_u32()?; }
298
299 let pixels = Self::unpack_v1(&mut reader, output_len)?;
300
301 if let Some((palette, palette_format)) = palette {
302 let data = CrxImageData::IndexedV1 {
303 pixels,
304 stride,
305 palette,
306 palette_format,
307 pixel_depth_bits: bits_per_pixel,
308 };
309 (ImageColorType::Bgr, data)
310 } else {
311 let mut trimmed = Vec::with_capacity(
312 row_bytes
313 .checked_mul(height)
314 .ok_or_else(|| anyhow::anyhow!("CRX v1 buffer size overflow"))?,
315 );
316 for row in 0..height {
317 let start = row
318 .checked_mul(stride)
319 .ok_or_else(|| anyhow::anyhow!("CRX v1 row offset overflow"))?;
320 let end = start
321 .checked_add(row_bytes)
322 .ok_or_else(|| anyhow::anyhow!("CRX v1 row slice overflow"))?;
323 if end > pixels.len() {
324 return Err(anyhow::anyhow!(
325 "CRX v1 image data is shorter than expected"
326 ));
327 }
328 trimmed.extend_from_slice(&pixels[start..end]);
329 }
330 let color_type = match bits_per_pixel {
331 24 => ImageColorType::Bgr,
332 32 => ImageColorType::Bgra,
333 _ => ImageColorType::Bgr,
334 };
335 (color_type, CrxImageData::Direct(trimmed))
336 }
337 } else {
338 let color_type = if header.bpp == 0 {
339 ImageColorType::Bgr
340 } else if header.bpp == 1 {
341 ImageColorType::Bgra
342 } else {
343 return Err(anyhow::anyhow!("Unsupported CRX bpp: {}", header.bpp));
344 };
345 let compressed_size = if (header.flags & 0x10) == 0 {
346 let len = reader.stream_length()?;
347 (len - reader.stream_position()?) as u32
348 } else {
349 reader.read_u32()?
350 };
351 let compressed_data = reader.read_exact_vec(compressed_size as usize)?;
352 let uncompressed = if compressed_data.starts_with(&[0x28, 0xb5, 0x2f, 0xfd]) {
353 let mut decoder = zstd::Decoder::new(MemReaderRef::new(&compressed_data))?;
354 let mut decompressed_data = Vec::new();
355 decoder.read_to_end(&mut decompressed_data)?;
356 decompressed_data
357 } else {
358 let mut decompressed_data = Vec::new();
359 flate2::read::ZlibDecoder::new(MemReaderRef::new(&compressed_data))
360 .read_to_end(&mut decompressed_data)?;
361 decompressed_data
362 };
363 (color_type, CrxImageData::RowEncoded(uncompressed))
364 };
365
366 Ok(CrxImage {
367 header,
368 color_type,
369 data,
370 compress_level: config.zlib_compression_level,
371 keep_original_bpp: config.circus_crx_keep_original_bpp,
372 zstd: config.circus_crx_zstd,
373 zstd_compression_level: config.zstd_compression_level,
374 row_type: config.circus_crx_mode.for_importing(),
375 canvas: config.circus_crx_canvas,
376 })
377 }
378
379 pub fn with_canvas(mut self, canvas: bool) -> Self {
381 self.canvas = canvas;
382 self
383 }
384
385 pub fn draw_diff(&self, diff: &Self) -> Result<ImageData> {
389 let base_header = &self.header;
390 let diff_header = &diff.header;
391 let (img_width, img_height) =
392 if base_header.clips.is_empty() && diff_header.clips.is_empty() {
393 (
394 (base_header.width + base_header.inner_x)
395 .max(diff_header.width + diff_header.inner_x),
396 (base_header.height + base_header.inner_y)
397 .max(diff_header.height + diff_header.inner_y),
398 )
399 } else {
400 if base_header.clips.is_empty() {
401 let clip = &diff_header.clips[0];
402 (clip.img_width, clip.img_height)
403 } else {
404 let clip = &base_header.clips[0];
405 (clip.img_width, clip.img_height)
406 }
407 };
408 let base = self.export_image()?;
409 let mut nw = draw_on_canvas(
410 base,
411 img_width as u32,
412 img_height as u32,
413 base_header.inner_x as u32,
414 base_header.inner_y as u32,
415 )?;
416 draw_on_img(
417 &mut nw,
418 &diff.export_image()?,
419 diff_header.inner_x as u32,
420 diff_header.inner_y as u32,
421 )?;
422 Ok(nw)
423 }
424
425 fn decode_row0(
426 dst: &mut Vec<u8>,
427 mut dst_p: usize,
428 src: &[u8],
429 mut src_p: usize,
430 width: u16,
431 pixel_size: u8,
432 ) -> Result<usize> {
433 let mut prev_p = dst_p;
434 for _ in 0..pixel_size {
435 dst[dst_p] = src[src_p];
436 dst_p += 1;
437 src_p += 1;
438 }
439 let remaining = width - 1;
440 for _ in 0..remaining {
441 for _ in 0..pixel_size {
442 dst[dst_p] = src[src_p].overflowing_add(dst[prev_p]).0;
443 dst_p += 1;
444 src_p += 1;
445 prev_p += 1;
446 }
447 }
448 Ok(src_p)
449 }
450
451 fn decode_row1(
452 dst: &mut Vec<u8>,
453 mut dst_p: usize,
454 src: &[u8],
455 mut src_p: usize,
456 width: u16,
457 pixel_size: u8,
458 mut prev_row_p: usize,
459 ) -> Result<usize> {
460 for _ in 0..width {
461 for _ in 0..pixel_size {
462 dst[dst_p] = src[src_p].overflowing_add(dst[prev_row_p]).0;
463 dst_p += 1;
464 src_p += 1;
465 prev_row_p += 1;
466 }
467 }
468 Ok(src_p)
469 }
470
471 fn decode_row2(
472 dst: &mut Vec<u8>,
473 mut dst_p: usize,
474 src: &[u8],
475 mut src_p: usize,
476 width: u16,
477 pixel_size: u8,
478 mut prev_row_p: usize,
479 ) -> Result<usize> {
480 for _ in 0..pixel_size {
481 dst[dst_p] = src[src_p];
482 dst_p += 1;
483 src_p += 1;
484 }
485 let remaining = width - 1;
486 for _ in 0..remaining {
487 for _ in 0..pixel_size {
488 dst[dst_p] = src[src_p].overflowing_add(dst[prev_row_p]).0;
489 dst_p += 1;
490 src_p += 1;
491 prev_row_p += 1;
492 }
493 }
494 Ok(src_p)
495 }
496
497 fn decode_row3(
498 dst: &mut Vec<u8>,
499 mut dst_p: usize,
500 src: &[u8],
501 mut src_p: usize,
502 width: u16,
503 pixel_size: u8,
504 mut prev_row_p: usize,
505 ) -> Result<usize> {
506 let count = width - 1;
507 prev_row_p += pixel_size as usize;
508 for _ in 0..count {
509 for _ in 0..pixel_size {
510 dst[dst_p] = src[src_p].overflowing_add(dst[prev_row_p]).0;
511 dst_p += 1;
512 src_p += 1;
513 prev_row_p += 1;
514 }
515 }
516 for _ in 0..pixel_size {
517 dst[dst_p] = src[src_p];
518 dst_p += 1;
519 src_p += 1;
520 }
521 Ok(src_p)
522 }
523
524 fn decode_row4(
525 dst: &mut Vec<u8>,
526 dst_p: usize,
527 src: &[u8],
528 mut src_p: usize,
529 width: u16,
530 pixel_size: u8,
531 ) -> Result<usize> {
532 for offset in 0..pixel_size {
533 let mut dst_c = dst_p + offset as usize;
534 let mut remaining = width;
535 let value = src[src_p];
536 src_p += 1;
537 dst[dst_c] = value;
538 dst_c += pixel_size as usize;
539 remaining -= 1;
540 if remaining == 0 {
541 continue;
542 }
543 if value == src[src_p] {
544 src_p += 1;
545 let count = src[src_p] as u16;
546 src_p += 1;
547 remaining -= count;
548 for _ in 0..count {
549 dst[dst_c] = value;
550 dst_c += pixel_size as usize;
551 }
552 }
553 while remaining > 0 {
554 let value = src[src_p];
555 src_p += 1;
556 dst[dst_c] = value;
557 dst_c += pixel_size as usize;
558 remaining -= 1;
559 if remaining == 0 {
560 break;
561 }
562 if value == src[src_p] {
563 src_p += 1;
564 let count = src[src_p] as u16;
565 src_p += 1;
566 remaining -= count;
567 for _ in 0..count {
568 dst[dst_c] = value;
569 dst_c += pixel_size as usize;
570 }
571 }
572 }
573 }
574 Ok(src_p)
575 }
576
577 fn read_v1_palette<T: Read>(reader: &mut T, raw_colors: usize) -> Result<Vec<u8>> {
578 if raw_colors == 0 {
579 return Err(anyhow::anyhow!("CRX v1 palette has zero colors"));
580 }
581 let color_size = if raw_colors == 0x0102 { 4usize } else { 3usize };
582 let mut colors = raw_colors;
583 if colors > 0x0100 {
584 colors = 0x0100;
585 }
586 let palette_size = colors
587 .checked_mul(color_size)
588 .ok_or_else(|| anyhow::anyhow!("CRX v1 palette size overflow"))?;
589 if palette_size == 0 {
590 return Err(anyhow::anyhow!("CRX v1 palette size is zero"));
591 }
592 let mut palette_raw = vec![0u8; palette_size];
593 reader.read_exact(&mut palette_raw)?;
594 let mut palette = Vec::with_capacity(colors * 3);
595 let mut pos = 0usize;
596 while pos < palette_raw.len() {
597 let r = palette_raw[pos];
598 let mut g = palette_raw[pos + 1];
599 let b = palette_raw[pos + 2];
600 if b == 0xFF && g == 0x00 && r == 0xFF {
601 g = 0xFF;
602 }
603 palette.push(r);
604 palette.push(g);
605 palette.push(b);
606 pos += color_size;
607 }
608 Ok(palette)
609 }
610
611 fn unpack_v1<T: Read>(reader: &mut T, output_len: usize) -> Result<Vec<u8>> {
612 const WINDOW_SIZE: usize = 0x10000;
613 const WINDOW_MASK: usize = WINDOW_SIZE - 1;
614 let mut window = vec![0u8; WINDOW_SIZE];
615 let mut win_pos: usize = 0;
616 let mut dst = vec![0u8; output_len];
617 let mut dst_pos = 0usize;
618 let mut flag: u16 = 0;
619 while dst_pos < output_len {
620 flag >>= 1;
621 if (flag & 0x100) == 0 {
622 let next = reader.read_u8()? as u16;
623 flag = next | 0xFF00;
624 }
625 if (flag & 1) != 0 {
626 let byte = reader.read_u8()?;
627 window[win_pos] = byte;
628 win_pos = (win_pos + 1) & WINDOW_MASK;
629 dst[dst_pos] = byte;
630 dst_pos += 1;
631 } else {
632 let control = reader.read_u8()?;
633 let (count, offset_value) = if control >= 0xC0 {
634 let next = reader.read_u8()? as usize;
635 let offset = (((control as usize) & 0x03) << 8) | next;
636 let count = 4 + (((control as usize) >> 2) & 0x0F);
637 (count, offset)
638 } else if (control & 0x80) != 0 {
639 let mut offset = (control & 0x1F) as usize;
640 let count = 2 + (((control as usize) >> 5) & 0x03);
641 if offset == 0 {
642 offset = reader.read_u8()? as usize;
643 }
644 (count, offset)
645 } else if control == 0x7F {
646 let count = 2 + reader.read_u16()? as usize;
647 let offset = reader.read_u16()? as usize;
648 (count, offset)
649 } else {
650 let offset = reader.read_u16()? as usize;
651 let count = control as usize + 4;
652 (count, offset)
653 };
654
655 let mut offset_pos = (win_pos.wrapping_sub(offset_value)) & WINDOW_MASK;
656 for _ in 0..count {
657 if dst_pos >= output_len {
658 break;
659 }
660 let value = window[offset_pos];
661 offset_pos = (offset_pos + 1) & WINDOW_MASK;
662 window[win_pos] = value;
663 win_pos = (win_pos + 1) & WINDOW_MASK;
664 dst[dst_pos] = value;
665 dst_pos += 1;
666 }
667 }
668 }
669 Ok(dst)
670 }
671
672 fn decode_image(
673 dst: &mut Vec<u8>,
674 src: &[u8],
675 width: u16,
676 height: u16,
677 pixel_size: u8,
678 encode_type: &mut Vec<u8>,
679 ) -> Result<()> {
680 let mut src_p = 0;
681 let mut dst_p = 0;
682 let mut prev_row_p = 0;
683 for _ in 0..height {
684 let data = src[src_p];
685 encode_type.push(data);
686 src_p += 1;
687 match data {
688 0 => {
689 src_p = Self::decode_row0(dst, dst_p, src, src_p, width, pixel_size)?;
690 }
691 1 => {
692 src_p =
693 Self::decode_row1(dst, dst_p, src, src_p, width, pixel_size, prev_row_p)?;
694 }
695 2 => {
696 src_p =
697 Self::decode_row2(dst, dst_p, src, src_p, width, pixel_size, prev_row_p)?;
698 }
699 3 => {
700 src_p =
701 Self::decode_row3(dst, dst_p, src, src_p, width, pixel_size, prev_row_p)?;
702 }
703 4 => {
704 src_p = Self::decode_row4(dst, dst_p, src, src_p, width, pixel_size)?;
705 }
706 _ => {
707 return Err(anyhow::anyhow!("Invalid row type: {}", data));
708 }
709 }
710 prev_row_p = dst_p;
711 dst_p += pixel_size as usize * width as usize;
712 }
713 Ok(())
714 }
715
716 fn encode_row0(dst: &mut Vec<u8>, src: &[u8], width: u16, pixel_size: u8, y: u16) {
717 let pixel_size = pixel_size as usize;
718 let mut src_p = y as usize * width as usize * pixel_size;
719 for _ in 0..pixel_size {
720 dst.push(src[src_p]);
721 src_p += 1;
722 }
723 for _ in 1..width {
724 for _ in 0..pixel_size {
725 dst.push(src[src_p].wrapping_sub(src[src_p - pixel_size]));
726 src_p += 1;
727 }
728 }
729 }
730
731 fn encode_row1(dst: &mut Vec<u8>, src: &[u8], width: u16, pixel_size: u8, y: u16) {
732 let pixel_size = pixel_size as usize;
733 let mut src_p = y as usize * width as usize * pixel_size;
734 let mut prev_row_p = (y as usize - 1) * width as usize * pixel_size;
735 for _ in 0..width {
736 for _ in 0..pixel_size {
737 dst.push(src[src_p].wrapping_sub(src[prev_row_p]));
738 src_p += 1;
739 prev_row_p += 1;
740 }
741 }
742 }
743
744 fn encode_row2(dst: &mut Vec<u8>, src: &[u8], width: u16, pixel_size: u8, y: u16) {
745 let pixel_size = pixel_size as usize;
746 let mut src_p = y as usize * width as usize * pixel_size;
747 let mut prev_row_p = (y as usize - 1) * width as usize * pixel_size;
748 for _ in 0..pixel_size {
749 dst.push(src[src_p]);
750 src_p += 1;
751 }
752 for _ in 1..width {
753 for _ in 0..pixel_size {
754 dst.push(src[src_p].wrapping_sub(src[prev_row_p]));
755 src_p += 1;
756 prev_row_p += 1;
757 }
758 }
759 }
760
761 fn encode_row3(dst: &mut Vec<u8>, src: &[u8], width: u16, pixel_size: u8, y: u16) {
762 let pixel_size = pixel_size as usize;
763 let mut src_p = y as usize * width as usize * pixel_size;
764 let mut prev_row_p = (y as usize - 1) * width as usize * pixel_size + pixel_size;
765 for _ in 0..width - 1 {
766 for _ in 0..pixel_size {
767 dst.push(src[src_p].wrapping_sub(src[prev_row_p]));
768 src_p += 1;
769 prev_row_p += 1;
770 }
771 }
772 for _ in 0..pixel_size {
773 dst.push(src[src_p]);
774 src_p += 1;
775 }
776 }
777
778 fn encode_row4(dst: &mut Vec<u8>, src: &[u8], width: u16, pixel_size: u8, y: u16) {
779 let pixel_size = pixel_size as usize;
780 let src_p = y as usize * width as usize * pixel_size;
781 for offset in 0..pixel_size {
782 let mut src_c = src_p + offset;
783 let mut remaining = width;
784 let value = src[src_c];
785 src_c += pixel_size;
786 dst.push(value);
787 remaining -= 1;
788 if remaining == 0 {
789 continue;
790 }
791 let mut count = 0;
792 loop {
793 if count as u16 >= remaining || count >= 255 || src[src_c] != value {
794 break;
795 }
796 src_c += pixel_size;
797 count += 1;
798 }
799 if count > 0 {
800 dst.push(value);
801 dst.push(count);
802 remaining -= count as u16;
803 }
804 while remaining > 0 {
805 let value = src[src_c];
806 src_c += pixel_size;
807 dst.push(value);
808 remaining -= 1;
809 if remaining == 0 {
810 break;
811 }
812 let mut count = 0;
813 loop {
814 if count as u16 >= remaining || count >= 255 || src[src_c] != value {
815 break;
816 }
817 src_c += pixel_size;
818 count += 1;
819 }
820 if count > 0 {
821 dst.push(value);
822 dst.push(count);
823 remaining -= count as u16;
824 }
825 }
826 }
827 }
828
829 fn encode_row_best(
830 dst: &mut Vec<u8>,
831 src: &[u8],
832 width: u16,
833 pixel_size: u8,
834 y: u16,
835 ) -> Result<()> {
836 let mut buf = Vec::with_capacity(width as usize * pixel_size as usize);
837 Self::encode_row0(&mut buf, src, width, pixel_size, y);
838 let mut compressed_len = {
839 let mut encoder =
840 flate2::write::ZlibEncoder::new(MemWriter::new(), flate2::Compression::fast());
841 encoder.write_all(&buf)?;
842 let compressed_data = encoder.finish()?;
843 compressed_data.into_inner().len()
844 };
845 let mut buf_row_type = 0;
846 for row_type in 1..5u8 {
847 if y == 0 && row_type < 4 {
848 continue;
849 }
850 let mut newbuf = Vec::with_capacity(width as usize * pixel_size as usize);
851 match row_type {
852 1 => Self::encode_row1(&mut newbuf, src, width, pixel_size, y),
853 2 => Self::encode_row2(&mut newbuf, src, width, pixel_size, y),
854 3 => Self::encode_row3(&mut newbuf, src, width, pixel_size, y),
855 4 => Self::encode_row4(&mut newbuf, src, width, pixel_size, y),
856 _ => return Err(anyhow::anyhow!("Invalid row type: {}", row_type)),
857 };
858 let new_compressed_len = {
859 let mut encoder =
860 flate2::write::ZlibEncoder::new(MemWriter::new(), flate2::Compression::fast());
861 encoder.write_all(&newbuf)?;
862 let compressed_data = encoder.finish()?;
863 compressed_data.into_inner().len()
864 };
865 if new_compressed_len < compressed_len {
866 compressed_len = new_compressed_len;
867 buf = newbuf;
868 buf_row_type = row_type;
869 }
870 }
871 dst.push(buf_row_type);
872 dst.extend_from_slice(&buf);
873 Ok(())
874 }
875
876 fn encode_image_best(src: &[u8], width: u16, height: u16, pixel_size: u8) -> Result<Vec<u8>> {
877 let size = width as usize * height as usize * pixel_size as usize + height as usize;
878 let mut dst = Vec::with_capacity(size);
879 for y in 0..height {
880 Self::encode_row_best(&mut dst, src, width, pixel_size, y)?;
881 }
882 Ok(dst)
883 }
884
885 fn encode_image_fixed(
886 src: &[u8],
887 width: u16,
888 height: u16,
889 pixel_size: u8,
890 row_type: u8,
891 ) -> Result<Vec<u8>> {
892 let size = width as usize * height as usize * pixel_size as usize + height as usize;
893 let mut dst = Vec::with_capacity(size);
894 for y in 0..height {
895 let row_type = if y == 0 && row_type != 0 && row_type != 4 {
896 0
897 } else {
898 row_type
899 };
900 dst.push(row_type);
901 match row_type {
902 0 => Self::encode_row0(&mut dst, src, width, pixel_size, y),
903 1 => Self::encode_row1(&mut dst, src, width, pixel_size, y),
904 2 => Self::encode_row2(&mut dst, src, width, pixel_size, y),
905 3 => Self::encode_row3(&mut dst, src, width, pixel_size, y),
906 4 => Self::encode_row4(&mut dst, src, width, pixel_size, y),
907 _ => return Err(anyhow::anyhow!("Invalid row type: {}", row_type)),
908 };
909 }
910 Ok(dst)
911 }
912
913 fn encode_image_origin(
914 src: &[u8],
915 width: u16,
916 height: u16,
917 pixel_size: u8,
918 row_type: &[u8],
919 ) -> Result<Vec<u8>> {
920 if row_type.len() != height as usize {
921 return Err(anyhow::anyhow!("Row type length does not match height"));
922 }
923 let size = width as usize * height as usize * pixel_size as usize + height as usize;
924 let mut dst = Vec::with_capacity(size);
925 for y in 0..height {
926 let row_type = row_type[y as usize];
927 dst.push(row_type);
928 match row_type {
929 0 => Self::encode_row0(&mut dst, src, width, pixel_size, y),
930 1 => Self::encode_row1(&mut dst, src, width, pixel_size, y),
931 2 => Self::encode_row2(&mut dst, src, width, pixel_size, y),
932 3 => Self::encode_row3(&mut dst, src, width, pixel_size, y),
933 4 => Self::encode_row4(&mut dst, src, width, pixel_size, y),
934 _ => return Err(anyhow::anyhow!("Invalid row type: {}", row_type)),
935 };
936 }
937 Ok(dst)
938 }
939
940 pub fn create_image<T: Write + Seek>(
946 mut data: ImageData,
947 mut writer: T,
948 config: &ExtraConfig,
949 ) -> Result<()> {
950 let header = Header {
951 inner_x: 0,
952 inner_y: 0,
953 width: data.width as u16,
954 height: data.height as u16,
955 version: 2,
956 flags: 0x10, bpp: match data.color_type {
958 ImageColorType::Bgr => 0,
959 ImageColorType::Bgra => 1,
960 ImageColorType::Rgb => {
961 convert_rgb_to_bgr(&mut data)?;
962 0
963 }
964 ImageColorType::Rgba => {
965 convert_rgba_to_bgra(&mut data)?;
966 1
967 }
968 _ => {
969 return Err(anyhow::anyhow!(
970 "Unsupported color type: {:?}",
971 data.color_type
972 ));
973 }
974 },
975 mode: 0,
976 clips: Vec::new(),
977 };
978 let pixel_size = data.color_type.bpp(1) as u8;
979 if data.color_type == ImageColorType::Bgra && header.mode != 1 {
980 let alpha_flip = if header.mode == 2 { 0 } else { 0xFF };
981 for i in (0..data.data.len()).step_by(4) {
982 let b = data.data[i];
983 let g = data.data[i + 1];
984 let r = data.data[i + 2];
985 let a = data.data[i + 3];
986 data.data[i] = a ^ alpha_flip;
987 data.data[i + 1] = b;
988 data.data[i + 2] = g;
989 data.data[i + 3] = r;
990 }
991 }
992 let mode = config.circus_crx_mode.for_creating();
993 let encoded = if mode.is_best() {
994 Self::encode_image_best(&data.data, header.width, header.height, pixel_size)?
995 } else if let CircusCrxMode::Fixed(mode) = mode {
996 Self::encode_image_fixed(&data.data, header.width, header.height, pixel_size, mode)?
997 } else {
998 return Err(anyhow::anyhow!(
999 "Unsupported row type for creating: {:?}",
1000 mode
1001 ));
1002 };
1003 let compressed = if config.circus_crx_zstd {
1004 let mut encoder = zstd::Encoder::new(MemWriter::new(), config.zstd_compression_level)?;
1005 encoder.write_all(&encoded)?;
1006 let compressed_data = encoder.finish()?;
1007 compressed_data.into_inner()
1008 } else {
1009 let mut encoder = flate2::write::ZlibEncoder::new(
1010 MemWriter::new(),
1011 flate2::Compression::new(config.zlib_compression_level),
1012 );
1013 encoder.write_all(&encoded)?;
1014 let compressed_data = encoder.finish()?;
1015 compressed_data.into_inner()
1016 };
1017 writer.write_all(b"CRXG")?;
1018 header.pack(&mut writer, false, Encoding::Utf8, &None)?;
1019 writer.write_u32(compressed.len() as u32)?;
1020 writer.write_all(&compressed)?;
1021 Ok(())
1022 }
1023}
1024
1025impl Script for CrxImage {
1026 fn default_output_script_type(&self) -> OutputScriptType {
1027 OutputScriptType::Json
1028 }
1029
1030 fn default_format_type(&self) -> FormatOptions {
1031 FormatOptions::None
1032 }
1033
1034 fn is_image(&self) -> bool {
1035 true
1036 }
1037
1038 fn export_image(&self) -> Result<ImageData> {
1039 let width = usize::from(self.header.width);
1040 let height = usize::from(self.header.height);
1041 let mut img = match &self.data {
1042 CrxImageData::RowEncoded(encoded) => {
1043 let pixel_size = self.color_type.bpp(1) as usize;
1044 let row_bytes = pixel_size
1045 .checked_mul(width)
1046 .ok_or_else(|| anyhow::anyhow!("Image row size overflow"))?;
1047 let data_size = row_bytes
1048 .checked_mul(height)
1049 .ok_or_else(|| anyhow::anyhow!("Image buffer size overflow"))?;
1050 let mut data = vec![0u8; data_size];
1051 let mut encode_type = Vec::with_capacity(height);
1052 Self::decode_image(
1053 &mut data,
1054 encoded,
1055 self.header.width,
1056 self.header.height,
1057 self.color_type.bpp(1) as u8,
1058 &mut encode_type,
1059 )?;
1060 if self.color_type.bpp(1) == 4 && self.header.mode != 1 {
1061 let alpha_flip = if self.header.mode == 2 { 0 } else { 0xFF };
1062 for chunk in data.chunks_mut(4) {
1063 let a = chunk[0];
1064 let b = chunk[1];
1065 let g = chunk[2];
1066 let r = chunk[3];
1067 chunk[0] = b;
1068 chunk[1] = g;
1069 chunk[2] = r;
1070 chunk[3] = a ^ alpha_flip;
1071 }
1072 }
1073 ImageData {
1074 width: self.header.width as u32,
1075 height: self.header.height as u32,
1076 depth: 8,
1077 color_type: self.color_type,
1078 data,
1079 }
1080 }
1081 CrxImageData::Direct(pixels) => {
1082 let mut data = pixels.clone();
1083 if self.color_type == ImageColorType::Bgra && self.header.mode != 1 {
1084 let alpha_flip = if self.header.mode == 2 { 0 } else { 0xFF };
1085 for chunk in data.chunks_mut(4) {
1086 let a = chunk[0];
1087 let b = chunk[1];
1088 let g = chunk[2];
1089 let r = chunk[3];
1090 chunk[0] = b;
1091 chunk[1] = g;
1092 chunk[2] = r;
1093 chunk[3] = a ^ alpha_flip;
1094 }
1095 }
1096 ImageData {
1097 width: self.header.width as u32,
1098 height: self.header.height as u32,
1099 depth: 8,
1100 color_type: self.color_type,
1101 data,
1102 }
1103 }
1104 CrxImageData::IndexedV1 {
1105 pixels,
1106 stride,
1107 palette,
1108 palette_format,
1109 pixel_depth_bits,
1110 } => {
1111 let total_pixels = width
1112 .checked_mul(height)
1113 .ok_or_else(|| anyhow::anyhow!("Image dimensions overflow"))?;
1114 let mut indexed = Vec::with_capacity(total_pixels);
1115 for row in 0..height {
1116 let start = row
1117 .checked_mul(*stride)
1118 .ok_or_else(|| anyhow::anyhow!("Row offset overflow"))?;
1119 let end = start
1120 .checked_add(width)
1121 .ok_or_else(|| anyhow::anyhow!("Row slice overflow"))?;
1122 if end > pixels.len() {
1123 return Err(anyhow::anyhow!("CRX v1 indexed data is truncated"));
1124 }
1125 indexed.extend_from_slice(&pixels[start..end]);
1126 }
1127 let image = convert_index_palette_to_normal_bitmap(
1128 &indexed,
1129 *pixel_depth_bits,
1130 palette,
1131 *palette_format,
1132 width,
1133 height,
1134 )?;
1135 image
1136 }
1137 };
1138
1139 if self.canvas {
1140 let (img_width, img_height) = if self.header.clips.is_empty() {
1141 (self.header.width as u32, self.header.height as u32)
1142 } else {
1143 let clip = &self.header.clips[0];
1144 (clip.img_width as u32, clip.img_height as u32)
1145 };
1146 img = draw_on_canvas(
1147 img,
1148 img_width,
1149 img_height,
1150 self.header.inner_x as u32,
1151 self.header.inner_y as u32,
1152 )?;
1153 }
1154 Ok(img)
1155 }
1156
1157 fn import_image<'a>(
1158 &'a self,
1159 mut data: ImageData,
1160 _filename: &str,
1161 mut file: Box<dyn WriteSeek + 'a>,
1162 ) -> Result<()> {
1163 let mut color_type = match data.color_type {
1164 ImageColorType::Bgr => ImageColorType::Bgr,
1165 ImageColorType::Bgra => ImageColorType::Bgra,
1166 ImageColorType::Rgb => {
1167 convert_rgb_to_bgr(&mut data)?;
1168 ImageColorType::Bgr
1169 }
1170 ImageColorType::Rgba => {
1171 convert_rgba_to_bgra(&mut data)?;
1172 ImageColorType::Bgra
1173 }
1174 _ => {
1175 return Err(anyhow::anyhow!(
1176 "Unsupported color type: {:?}",
1177 data.color_type
1178 ));
1179 }
1180 };
1181 if data.width != self.header.width as u32 {
1182 return Err(anyhow::anyhow!(
1183 "Image width does not match: expected {}, got {}",
1184 self.header.width,
1185 data.width
1186 ));
1187 }
1188 if data.height != self.header.height as u32 {
1189 return Err(anyhow::anyhow!(
1190 "Image height does not match: expected {}, got {}",
1191 self.header.height,
1192 data.height
1193 ));
1194 }
1195 if data.depth != 8 {
1196 return Err(anyhow::anyhow!("Image depth must be 8, got {}", data.depth));
1197 }
1198 if data.color_type != self.color_type && self.keep_original_bpp {
1199 if self.color_type == ImageColorType::Bgr {
1200 convert_bgra_to_bgr(&mut data)?;
1201 } else if self.color_type == ImageColorType::Bgra {
1202 convert_bgr_to_bgra(&mut data)?;
1203 } else {
1204 return Err(anyhow::anyhow!(
1205 "Unsupported color type for import: {:?}",
1206 self.color_type
1207 ));
1208 }
1209 color_type = self.color_type;
1210 }
1211 let mut new_header = self.header.clone();
1212 new_header.bpp = match color_type {
1213 ImageColorType::Bgr => 0,
1214 ImageColorType::Bgra => 1,
1215 _ => return Err(anyhow::anyhow!("Unsupported color type: {:?}", color_type)),
1216 };
1217 if new_header.version == 1 {
1218 new_header.version = 2; }
1220 new_header.flags |= 0x10; let pixel_size = color_type.bpp(1) as u8;
1222 if color_type == ImageColorType::Bgra && self.header.mode != 1 {
1223 let alpha_flip = if self.header.mode == 2 { 0 } else { 0xFF };
1224 for i in (0..data.data.len()).step_by(4) {
1225 let b = data.data[i];
1226 let g = data.data[i + 1];
1227 let r = data.data[i + 2];
1228 let a = data.data[i + 3];
1229 data.data[i] = a ^ alpha_flip;
1230 data.data[i + 1] = b;
1231 data.data[i + 2] = g;
1232 data.data[i + 3] = r;
1233 }
1234 }
1235 let encoded = if self.row_type.is_origin() && self.data.is_row_encoded() {
1236 let mut row_type = Vec::with_capacity(self.header.height as usize);
1237 let pixel_size_bytes = self.color_type.bpp(1) as usize;
1238 let row_len = pixel_size_bytes
1239 .checked_mul(self.header.width as usize)
1240 .ok_or_else(|| anyhow::anyhow!("Row length overflow"))?
1241 .checked_add(1)
1242 .ok_or_else(|| anyhow::anyhow!("Row length overflow"))?;
1243 let buffer = match &self.data {
1244 CrxImageData::RowEncoded(buf) => buf,
1245 _ => {
1246 return Err(anyhow::anyhow!(
1247 "Original row type information is unavailable"
1248 ));
1249 }
1250 };
1251 let mut cur_pos = 0usize;
1252 for _ in 0..self.header.height {
1253 if cur_pos >= buffer.len() {
1254 return Err(anyhow::anyhow!("Row type offset exceeds buffer length"));
1255 }
1256 let cur_row_type = buffer[cur_pos];
1257 row_type.push(cur_row_type);
1258 let true_row_len = if cur_row_type < 4 {
1259 row_len
1260 } else {
1261 let mut offset = cur_pos + 1;
1262 for _ in 0..pixel_size {
1263 let mut remaing = self.header.width;
1264 let value = buffer[offset];
1265 offset += 1;
1266 remaing -= 1;
1267 if remaing == 0 {
1268 continue;
1269 }
1270 if value == buffer[offset] {
1271 offset += 1;
1272 let count = buffer[offset] as u16;
1273 offset += 1;
1274 remaing = remaing
1275 .checked_sub(count)
1276 .ok_or_else(|| anyhow::anyhow!("Row run-length overflow"))?;
1277 }
1278 while remaing > 0 {
1279 let value = buffer[offset];
1280 offset += 1;
1281 remaing -= 1;
1282 if remaing == 0 {
1283 break;
1284 }
1285 if value == buffer[offset] {
1286 offset += 1;
1287 let count = buffer[offset] as u16;
1288 offset += 1;
1289 remaing = remaing
1290 .checked_sub(count)
1291 .ok_or_else(|| anyhow::anyhow!("Row run-length overflow"))?;
1292 }
1293 }
1294 }
1295 offset - cur_pos
1296 };
1297 cur_pos = cur_pos
1298 .checked_add(true_row_len)
1299 .ok_or_else(|| anyhow::anyhow!("Row type offset overflow"))?;
1300 }
1301 Self::encode_image_origin(
1302 &data.data,
1303 new_header.width,
1304 new_header.height,
1305 pixel_size,
1306 &row_type,
1307 )?
1308 } else if self.row_type.is_best()
1309 || (self.row_type.is_origin() && !self.data.is_row_encoded())
1310 {
1311 Self::encode_image_best(&data.data, new_header.width, new_header.height, pixel_size)?
1312 } else if let CircusCrxMode::Fixed(mode) = self.row_type {
1313 Self::encode_image_fixed(
1314 &data.data,
1315 new_header.width,
1316 new_header.height,
1317 pixel_size,
1318 mode,
1319 )?
1320 } else {
1321 return Err(anyhow::anyhow!(
1322 "Unsupported row type for import: {:?}",
1323 self.row_type
1324 ));
1325 };
1326 let compressed = if self.zstd {
1327 let mut encoder = zstd::Encoder::new(MemWriter::new(), self.zstd_compression_level)?;
1328 encoder.write_all(&encoded)?;
1329 let compressed_data = encoder.finish()?;
1330 compressed_data.into_inner()
1331 } else {
1332 let mut encoder = flate2::write::ZlibEncoder::new(
1333 MemWriter::new(),
1334 flate2::Compression::new(self.compress_level),
1335 );
1336 encoder.write_all(&encoded)?;
1337 let compressed_data = encoder.finish()?;
1338 compressed_data.into_inner()
1339 };
1340 file.write_all(b"CRXG")?;
1341 new_header.pack(&mut file, false, Encoding::Utf8, &None)?;
1342 file.write_u32(compressed.len() as u32)?;
1343 file.write_all(&compressed)?;
1344 Ok(())
1345 }
1346}
1347
1348fn draw_on_img(base: &mut ImageData, diff: &ImageData, left: u32, top: u32) -> Result<()> {
1349 if base.color_type != diff.color_type {
1350 return Err(anyhow::anyhow!(
1351 "Color types do not match: {:?} vs {:?}",
1352 base.color_type,
1353 diff.color_type
1354 ));
1355 }
1356 let bpp = base.color_type.bpp(1) as usize;
1357 let base_stride = base.width as usize * bpp;
1358 let diff_stride = diff.width as usize * bpp;
1359
1360 for y in 0..diff.height {
1361 let base_y = top + y;
1362 if base_y >= base.height {
1363 continue; }
1365
1366 for x in 0..diff.width {
1367 let base_x = left + x;
1368 if base_x >= base.width {
1369 continue; }
1371
1372 let base_index = (base_y as usize * base_stride) + (base_x as usize * bpp);
1373 let diff_index = (y as usize * diff_stride) + (x as usize * bpp);
1374
1375 let diff_pixel = &diff.data[diff_index..diff_index + bpp];
1376 let base_pixel_orig = base.data[base_index..base_index + bpp].to_vec();
1377 let mut b = base_pixel_orig[0];
1378 let mut g = base_pixel_orig[1];
1379 let mut r = base_pixel_orig[2];
1380 wrapping! {
1381 b += diff_pixel[0];
1382 g += diff_pixel[1];
1383 r += diff_pixel[2];
1384 }
1385 base.data[base_index] = b;
1386 base.data[base_index + 1] = g;
1387 base.data[base_index + 2] = r;
1388 if bpp == 4 {
1389 let mut a = base_pixel_orig[3];
1390 wrapping! {
1391 a -= diff_pixel[3];
1392 }
1393 base.data[base_index + 3] = a;
1394 }
1395 }
1396 }
1397 Ok(())
1398}